from django.shortcuts import render
from django.views import View
import json, pickle, base64
from django import http
from django_redis import get_redis_connection

from goods.models import SKU
from meiduo_mall.utils.response_code import RETCODE


class CartsView(View):
    """购物车"""
    def post(self, request):
        """购物车新增"""
        # 接收请求数据
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        count = json_dict.get('count')
        selected = json_dict.get('selected', True)
        # 校验
        try:
            sku = SKU.objects.get(id=sku_id, is_launched=True)
        except SKU.DoesNotExist:
            return http.HttpResponseForbidden('sku_id不存在')
        try:
            count = abs(int(count))
        except Exception as e:
            return http.HttpResponseForbidden('类型有误')
        if isinstance(selected, bool) is False:
            return http.HttpResponseForbidden('类型有误')

        user = request.user
        # 判断用户是否登录
        if user.is_authenticated:
            # 登录用户存储购物车数据到redis
            """
            hash: {sku_id: count}
            set: {sku_id}
            """
            # 创建redis连接对象
            redis_conn = get_redis_connection('carts')
            pl = redis_conn.pipeline()
            # 向hash存储
            # redis_conn.hincrby(key, field, value)
            pl.hincrby('carts_%s' % user.id, sku_id, count)
            # 如果当前商品是勾选就将勾选商品id 存储到set
            if selected:
                # redis_conn.sadd(key, value)
                pl.sadd('selected_%s' % user.id, sku_id)

            pl.execute()  # 执行管道
            # 响应
            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})
        else:
            # 未登录用户存储购物车数据到cookie
            """
            {
                sku_id_1: {'count': 1, 'selected': True}
            }
            """
            # 获取cookie中的购物车数据
            cart_str = request.COOKIES.get('carts')
            # 判断是否获取到cookie购物车数据
            if cart_str:
                # 如果有,将cookie字符串数据转换回字典
                cart_str_bytes = cart_str.encode()  # 将str --> bytes
                cart_str_bytes_un = base64.b64decode(cart_str_bytes)  # 将bytes--> bytes uncode
                cart_dict = pickle.loads(cart_str_bytes_un)  # bytes uncode ---> dict
                # 判断本次要添加的商品之前是否添加过
                if sku_id in cart_dict:
                    origin_count = cart_dict[sku_id]['count']
                    count += origin_count
            else:
                # 如果没有, 准备新字典
                cart_dict = {}

            # 向cookie购物车大字典添加新数据
            cart_dict[sku_id] = {'count': count, 'selected': selected}
            # 将cookie购物车大字典再转回字符串
            cart_str_bytes_un = pickle.dumps(cart_dict)  # 将dict --> bytes uncode
            cart_str_bytes = base64.b64encode(cart_str_bytes_un)  # bytes uncode --> bytes
            cart_str = cart_str_bytes.decode()   # bytes ---> str
            # 创建响应对象
            response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})
            # 设置cookie
            response.set_cookie('carts', cart_str, max_age=3600 * 720)
            return response

    def get(self, request):
        """购物车展示"""
        user = request.user
        # 判断用户是否登录
        if user.is_authenticated:
            # 登录用户从redis获取购物车数据
            redis_conn = get_redis_connection('carts')
            # 获取hash数据  {sku_id_1: 2, sku_id_2: 1}
            redis_carts = redis_conn.hgetall('carts_%s' % user.id)
            # 获取set集合数据 {sku_id_1, sku_id_2}
            selected_ids = redis_conn.smembers('selected_%s' % user.id)
            # 将redis购物车数据格式转换成和cookie购物车数据格式一样 {sku_id_1: {'count': 2, 'selected': True}}
            cart_dict = {}  # 此变量用来包装登录用户购物车数据 格式最终和cookie购物车一致
            for sku_id_bytes in redis_carts:
                cart_dict[int(sku_id_bytes)] = {
                    'count': int(redis_carts[sku_id_bytes]),
                    'selected': sku_id_bytes in selected_ids
                }

        else:
            # 未登录用户从cookie中获取数据数据
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                # 如果有cookie购物车数据,将str ---> dict
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                return render(request, 'cart.html')

        sku_list =[]  # 包装购物车所有数据
        sku_ids = cart_dict.keys()
        # 通过sku_id查询sku模型
        sku_qs = SKU.objects.filter(id__in=sku_ids)
        # 遍历sku查询集模型转字典
        for sku_model in sku_qs:
            count = cart_dict[sku_model.id]['count']
            sku_list.append({
                'id': sku_model.id,
                'name': sku_model.name,
                'price': str(sku_model.price),  # 为了方便js json解析
                'default_image_url': sku_model.default_image.url,
                'count': count,  # 商品数量
                'selected': str(cart_dict[sku_model.id]['selected']),  # 是否勾选
                'amount': str(sku_model.price * count)   # 小计
            })

        context = {
            'cart_skus': sku_list
        }
        return render(request, 'cart.html', context)

    def put(self, request):
        # 接收
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        count = json_dict.get('count')
        selected = json_dict.get('selected')

        # 校验
        try:
            sku_model = SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.HttpResponseForbidden('sku_id不存在')
        try:
            count = int(count)
        except:
            return http.HttpResponseForbidden('类型有误')
        if isinstance(selected, bool) is False:
            return http.HttpResponseForbidden('类型有误')

        user = request.user
        # 将修改后的购物车商品包装一个大字典响应
        cart = {
                'id': sku_model.id,
                'name': sku_model.name,
                'price': sku_model.price,  # 为了方便js json解析
                'default_image_url': sku_model.default_image.url,
                'count': count,  # 商品数量
                'selected': selected,  # 是否勾选
                'amount': sku_model.price * count   # 小计
            }
        response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'cart_sku': cart})

        # 判断用户是否登录
        if user.is_authenticated:
            # 登录用户修改redis购物车数据
            # 创建redis连接对象
            redis_conn = get_redis_connection('carts')
            # 修改hash数据
            # redis_conn.hset(key, field, value)
            pl = redis_conn.pipeline()
            pl.hset('carts_%s' % user.id, sku_id, count)
            # 判断当前商品是否勾选,勾选添加到set中不勾选将它从set中删除
            if selected:
                pl.sadd('selected_%s' % user.id, sku_id)
            else:
                pl.srem('selected_%s' % user.id, sku_id)
            pl.execute()

        else:
            # 未登录用户修改cookie购物车数据
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                return http.HttpResponseForbidden('cookie购物车那里去?')
            # 修改
            cart_dict[sku_id] = {'count': count, 'selected': selected}
            # 将cart_dict  ---> cart_str
            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            response.set_cookie('carts', cart_str, max_age=3600 * 720)
        return response

    def delete(self, request):
        """购物车删除"""
        # 接收
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        # 校验
        try:
            sku = SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.HttpResponseForbidden('sku_id不存在')
        user = request.user
        # 创建响应对象
        response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})
        # 判断用户是否登录
        if user.is_authenticated:
            # 登录用户删除redis购物车数据
            # 创建redis连接对象
            redis_conn = get_redis_connection('carts')
            pl = redis_conn.pipeline()
            # 删除hash  hdel
            pl.hdel('carts_%s' % user.id, sku_id)
            # 删除set srem
            pl.srem('selected_%s' % user.id, sku_id)
            pl.execute()

        else:
            # 未登录用户删除cookie购物车数据
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                return http.HttpResponseForbidden('cookie购物车哪去了?')
            # 判断cart_dict字典中是否有sku_id key
            if sku_id in cart_dict:
                del cart_dict[sku_id]  # {}

                if not cart_dict:  # 判断字典中是否还有数据,如果没有数据了,直接将cookie购物车删除
                    response.delete_cookie('carts')
                    return response

                cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
                response.set_cookie('carts', cart_str, max_age=3600 * 720)
        # 响应
        return response


class CartsSelectedView(View):
    """购物车全选"""
    def put(self, request):
        # 接收
        json_dict = json.loads(request.body.decode())
        selected = json_dict.get('selected')
        # 校验
        if isinstance(selected, bool) is False:
            return http.HttpResponseForbidden('类型有误')
        user = request.user
        response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})
        # 判断用户是否登录
        if user.is_authenticated:
            # 登录用户操作redis数据
            redis_conn = get_redis_connection('carts')
            if selected:  # 全选
                redis_carts = redis_conn.hgetall('carts_%s' % user.id)
                sku_ids = redis_carts.keys()  # [2, 3]
                redis_conn.sadd('selected_%s' % user.id, *sku_ids)
            else:
                # 取消全选
                redis_conn.delete('selected_%s' % user.id)
            pass
        else:
            # 未登录用户操作cookie数据
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                return http.HttpResponseForbidden('没有cookie购物车')
            # for循环将selected 键对应值全部改成True或False
            for sku_id in cart_dict:
                cart_dict[sku_id]['selected'] = selected

            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            response.set_cookie('carts', cart_str, max_age=3600 * 720)

        return response


class CartsSimpleView(View):
    """简单版购物车展示"""
    def get(self, request):
        """购物车展示"""
        user = request.user
        # 判断用户是否登录
        if user.is_authenticated:
            # 登录用户从redis获取购物车数据
            redis_conn = get_redis_connection('carts')
            # 获取hash数据  {sku_id_1: 2, sku_id_2: 1}
            redis_carts = redis_conn.hgetall('carts_%s' % user.id)
            # 获取set集合数据 {sku_id_1, sku_id_2}
            selected_ids = redis_conn.smembers('selected_%s' % user.id)
            # 将redis购物车数据格式转换成和cookie购物车数据格式一样 {sku_id_1: {'count': 2, 'selected': True}}
            cart_dict = {}  # 此变量用来包装登录用户购物车数据 格式最终和cookie购物车一致
            for sku_id_bytes in redis_carts:
                cart_dict[int(sku_id_bytes)] = {
                    'count': int(redis_carts[sku_id_bytes]),
                    'selected': sku_id_bytes in selected_ids
                }

        else:
            # 未登录用户从cookie中获取数据数据
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                # 如果有cookie购物车数据,将str ---> dict
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                # return render(request, 'cart.html')
                return http.JsonResponse({'code': RETCODE.NODATAERR, 'errmsg': '没有购物车数据'})

        sku_list =[]  # 包装购物车所有数据
        sku_ids = cart_dict.keys()
        # 通过sku_id查询sku模型
        sku_qs = SKU.objects.filter(id__in=sku_ids)
        # 遍历sku查询集模型转字典
        for sku_model in sku_qs:
            count = cart_dict[sku_model.id]['count']
            sku_list.append({
                'id': sku_model.id,
                'name': sku_model.name,
                'default_image_url': sku_model.default_image.url,
                'count': count,  # 商品数量
            })

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'cart_skus': sku_list})

